package org.mozhu.mboot.core.web.bind.resolver;

import org.apache.commons.collections.CollectionUtils;
import org.mozhu.mboot.core.data.SearchFilters;
import org.mozhu.mboot.core.data.Searchable;
import org.mozhu.mboot.core.data.filter.SearchFilter;
import org.mozhu.mboot.core.web.bind.annotation.SearchableDefaults;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 请求查询参数字符串及分页/排序参数绑定到Searchable
 * 
 * <pre>
 *     查询参数格式如下：
 *     1.1、默认查询字符串
 *         search_like_baseInfo.realname=zhang
 *         search_lt_age=12
 *         排序及分页请参考 {@link PageableMethodArgumentResolver}
 *     1.2、控制器处理方法写法
 *        public void test(Searchable searchable);
 * 
 *     2.1、自定义查询字符串
 *         foo_search_like_baseInfo.realname=zhang
 *         foo_search_lt_age=12
 *         test_search_gt_age=12
 *         排序及分页请参考 {@link PageableMethodArgumentResolver}
 *     2.2、控制器处理方法写法
 *        public void test(@Qualifier("foo") Searchable searchable1, @Qualifier("test") Searchable searchable2);
 * 
 *     3.1、禁用查询时分页及排序
 *          public void test(@Search(page = false, sort = false) Searchable searchable);
 * </pre>
 */
public class SearchableMethodArgumentResolver extends BaseMethodArgumentResolver {

	private static final PageableMethodArgumentResolver DEFAULT_PAGEABLE_RESOLVER = new PageableMethodArgumentResolver();

	private String prefix = SearchFilters.DEFAULT_SEARCH_PREFIX;

	/**
	 * 设置查询参数前缀
	 *
	 * @param prefix
	 */
	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	/**
	 * 分页参数解析器
	 */
	private PageableMethodArgumentResolver pageableMethodArgumentResolver = DEFAULT_PAGEABLE_RESOLVER;

	public void setPageableMethodArgumentResolver(PageableMethodArgumentResolver pageableMethodArgumentResolver) {
		this.pageableMethodArgumentResolver = pageableMethodArgumentResolver;
	}

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return Searchable.class.isAssignableFrom(parameter.getParameterType());
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		String searchPrefix = getSearchPrefix(parameter);
		List<SearchFilter> searchFilters = SearchFilters.parse((HttpServletRequest) webRequest.getNativeRequest(), searchPrefix);
		boolean hasCustomSearchFilter = CollectionUtils.isNotEmpty(searchFilters);

		SearchableDefaults searchDefaults = getSearchableDefaults(parameter);
		boolean needMergeDefault = searchDefaults != null && searchDefaults.merge();

		Searchable searchable = null;
		// 自定义覆盖默认
		if (needMergeDefault || !hasCustomSearchFilter) {
			searchable = getDefaultFromAnnotation(searchDefaults);
		}
		if (hasCustomSearchFilter) {
			if (searchable == null) {
				searchable = Searchable.newSearchable();
			}
			searchable.addSearchFilters(searchFilters);
		}

		Pageable pageable = (Pageable) pageableMethodArgumentResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
		// 默认分页及排序
		if (searchDefaults == null) {
			searchable.setPage(pageable);
		}
		// needPage=true 分页及排序
		if (searchDefaults != null && searchDefaults.needPage()) {
			searchable.setPage(pageable);
		}
		// needPage=false needSort=true 不要分页，但排序
		if (searchDefaults != null && !searchDefaults.needPage() && searchDefaults.needSort()) {
			searchable.addSort(pageable.getSort());
		}

		return searchable;
	}

	private String getSearchPrefix(MethodParameter parameter) {
		Qualifier qualifier = parameter.getParameterAnnotation(Qualifier.class);
		if (qualifier != null) {
			return new StringBuilder(((Qualifier) qualifier).value()).append("_").append(prefix).toString();
		}
		return prefix;
	}

	private SearchableDefaults getSearchableDefaults(MethodParameter parameter) {
		// 首先从参数上找
		SearchableDefaults searchDefaults = parameter.getParameterAnnotation(SearchableDefaults.class);
		// 找不到从方法上找
		if (searchDefaults == null) {
			searchDefaults = parameter.getMethodAnnotation(SearchableDefaults.class);
		}
		return searchDefaults;
	}

	private Searchable getDefaultFromAnnotation(SearchableDefaults searchableDefaults) {
		Searchable searchable = defaultSearchable(searchableDefaults);
		if (searchable != null) {
			return searchable;
		}
		return Searchable.newSearchable();
	}

	private Searchable defaultSearchable(SearchableDefaults searchableDefaults) {
		if (searchableDefaults == null) {
			return null;
		}
		Searchable searchable = Searchable.newSearchable();
		for (String searchParam : searchableDefaults.value()) {
			String[] searchPair = searchParam.split("=");
			String paramName = searchPair[0];
			String paramValue = searchPair[1];
			searchable.addSearchParam(paramName, paramValue);
		}
		return searchable;
	}

}
